use axum::{
    async_trait,
    body::Body,
    extract::FromRequestParts,
    http::{request::Parts, Response, StatusCode},
    response::IntoResponse,
    RequestPartsExt,
};
use axum_extra::{
    headers::{authorization::Bearer, Authorization},
    TypedHeader,
};
use config::Map;
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

use super::autherror::AuthError;


pub struct Keys {
    pub encoding: EncodingKey,
    pub decoding: DecodingKey,
}
impl Keys {
    fn new(secret: &[u8]) -> Self {
        Self {
            encoding: EncodingKey::from_secret(secret),
            decoding: DecodingKey::from_secret(secret),
        }
    }
}
pub static KEYS: Lazy<Keys> = Lazy::new(|| {
    let secret = match std::env::var("USER_SECRET_KEY") {
        Ok(secert) => secert,
        Err(_) => dotenv::var("jwt.user_secret_key").expect("Failed to get user_secret_key"),
    };
    Keys::new(secret.as_bytes())
});

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub id: i64,
    pub name: String,
    pub exp: Option<i64>,
    pub sub: Option<i64>,
}

impl Claims {
    pub fn new(claims: Claims) -> Result<String, Box<dyn std::error::Error>> {
        let header = Header::new(Algorithm::HS256);

        let ttl = dotenv::var("jwt.ttl")
            .map(|ttl_str| ttl_str.parse::<i64>().unwrap_or(60))
            .unwrap_or(60);

        let exp = (chrono::Utc::now() + chrono::Duration::minutes(ttl)).timestamp();
        let claims = Claims {
            exp: Some(exp),
            ..claims
        };
        // 创建token
        let token = encode(&header, &claims, &KEYS.encoding)?;
        Ok(token)
    }
}

#[async_trait]
impl<S> FromRequestParts<S> for Claims
where
    S: Send + Sync,
{
    type Rejection = AuthError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let authorization = parts
            .headers
            .get("Authorization")
            .ok_or(AuthError::MissingCredentials)?;

        // Check if the Authorization header contains a Bearer token
        let token = authorization
            .to_str()
            .map_err(|_| AuthError::InvalidToken)?;
        // Decode the JWT token (you need to provide the secret key here)
        let validation = Validation::new(Algorithm::HS256); // Choose your JWT algorithm here

        let decoded_token =
            decode::<Claims>(token, &KEYS.decoding, &validation).map_err(|e| match e.kind() {
                jsonwebtoken::errors::ErrorKind::ExpiredSignature => AuthError::ExpiredToken,
                _ => AuthError::InvalidToken,
            })?;

        tracing::info!("当前用户: {}", decoded_token.claims.id);

        // Return the Claims
        Ok(decoded_token.claims)
    }
}
